﻿using System;
using System.Globalization;

namespace Microsoft.Xna.Framework.Graphics
{
	/// <summary>
	/// Contains rasterizer state, which determines how to convert vector data (shapes) into raster data (pixels).
	/// </summary>
	public class RasterizerState : GraphicsResource
	{
		#region Fields
		/// <summary>
		/// A built-in state object with settings for culling primitives with clockwise winding order.
		/// </summary>
		public static readonly RasterizerState CullClockwise = new RasterizerState(CullMode.CullClockwiseFace, "RasterizerState.CullClockwise");

		/// <summary>
		/// A built-in state object with settings for culling primitives with counter-clockwise winding order.
		/// </summary>
		public static readonly RasterizerState CullCounterClockwise = new RasterizerState(CullMode.CullCounterClockwiseFace, "RasterizerState.CullCounterClockwise");

		/// <summary>
		/// A built-in state object with settings for not culling any primitives.
		/// </summary>
		public static readonly RasterizerState CullNone = new RasterizerState(CullMode.None, "RasterizerState.CullNone");

		private CullMode cullMode;
		private float depthBias;
		private FillMode fillMode;
		internal bool isBound;
		private bool multiSampleAntiAlias;
		private bool scissorTestEnable;
		private float slopeScaleDepthBias;
		#endregion

		#region Properties
		/// <summary>
		/// Specifies the conditions for culling or removing triangles. The default value is <see cref="F:CullMode.CounterClockwise"/>.
		/// </summary>
		public CullMode CullMode
		{
			get { return cullMode; }
			set
			{
				ThrowIfBound();
				cullMode = value;
			}
		}

		/// <summary>
		/// Sets or retrieves the depth bias for polygons, which is the amount of bias to apply to the depth of a primitive to alleviate depth testing problems for primitives of similar depth. The default value is 0.
		/// </summary>
		public float DepthBias
		{
			get { return depthBias; }
			set
			{
				ThrowIfBound();
				depthBias = value;
			}
		}

		/// <summary>
		/// The fill mode, which defines how a triangle is filled during rendering. The default is <see cref="F:FillMode.Solid"/>.
		/// </summary>
		public FillMode FillMode
		{
			get { return fillMode; }
			set
			{
				ThrowIfBound();
				fillMode = value;
			}
		}

		/// <summary>
		/// Enables or disables multisample antialiasing. The default is true.
		/// </summary>
		public bool MultiSampleAntiAlias
		{
			get { return multiSampleAntiAlias; }
			set
			{
				ThrowIfBound();
				multiSampleAntiAlias = value;
			}
		}

		/// <summary>
		/// Enables or disables scissor testing. The default is false.
		/// </summary>
		public bool ScissorTestEnable
		{
			get { return scissorTestEnable; }
			set
			{
				ThrowIfBound();
				scissorTestEnable = value;
			}
		}

		/// <summary>
		/// Gets or sets a bias value that takes into account the slope of a polygon. This bias value is applied to coplanar primitives to reduce aliasing and other rendering artifacts caused by z-fighting. The default is 0.
		/// </summary>
		public float SlopeScaleDepthBias
		{
			get { return slopeScaleDepthBias; }
			set
			{
				ThrowIfBound();
				slopeScaleDepthBias = value;
			}
		}
		#endregion

		#region Constructors
		/// <summary>
		/// Initializes a new instance of the rasterizer class.
		/// </summary>
		public RasterizerState()
		{
			cullMode = CullMode.CullCounterClockwiseFace;
			depthBias = 0f;
			fillMode = FillMode.Solid;
			isBound = false;
			multiSampleAntiAlias = true;
			scissorTestEnable = false;
			slopeScaleDepthBias = 0f;
		}

		private RasterizerState(CullMode cullMode, string name)
		{
			this.cullMode = cullMode;
			depthBias = 0f;
			fillMode = FillMode.Solid;
			isBound = true;
			multiSampleAntiAlias = true;
			base.Name = name;
			scissorTestEnable = false;
			slopeScaleDepthBias = 0f;
		}
		#endregion

		private void ThrowIfBound()
		{
			if (this.isBound)
			{
				throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, FrameworkResources.BoundStateObject, typeof(RasterizerState).Name));
			}
		}
	}
}
